/**
 * wuqibo 2022.10.09
 * 2D模拟3D圆形漂浮旋转抽奖(实例：渔场1魔蟹的宝库)
 */

using DG.Tweening;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace BToolkit
{
    [ExecuteInEditMode]
    public class PrizeWheel2D : MonoBehaviour
    {
        private static bool useCurve = true;

        [Header("当前角度")]
        public float angle;
        [Header("转速曲线")]
        public AnimationCurve rotateCurve;
        [Header("旋转圈数")]
        public int round = 5;
        [Header("旋转时间")]
        public float duration = 6;
        [Header("大圆尺寸")]
        public Vector2 size;
        [Header("最远缩放值")]
        public float minScale = 0.7f;
        [Header("最近缩放值")]
        public float maxScale = 1.5f;
        [Header("所有元素")]
        public RectTransform[] cells;
        [Header("闲置时自转速度")]
        public float autoSpeed = 10;

        public Action onRotatedCell;

        private float deltaAngle, preAngle, cellRadianListen;
        private float[] preYs = new float[0];
        public bool isRunning { private set; get; }
        ETTask task;
        float runningFalseTimer;
        bool isInitedLayer, isCurveRunning;
        float curveTime, fromAngle, toAngle;
        //缩放（模拟远近）的线性方程
        private Line scaleLine;

        void Start()
        {
            Init();
        }

        private void Init()
        {
            if (preYs.Length != cells.Length)
            {
                preYs = new float[cells.Length];
            }
            deltaAngle = 360 / (float)cells.Length;
            //传入两个点构建线性方程
            scaleLine = new Line(-size.y, maxScale, size.y, minScale);
        }

        void Update()
        {
#if UNITY_EDITOR
            Init();

            //鼠标右键点击测试效果
            if (Input.GetMouseButtonDown(1))
            {
                StartRun(180).Coroutine();
            }
#endif
            for (int i = 0; i < cells.Length; i++)
            {
                RectTransform cell = cells[i];
                Vector2 pos;
                float r = (angle - deltaAngle * i) * Mathf.Deg2Rad;
                pos.x = Mathf.Sin(r) * size.x;
                pos.y = Mathf.Cos(r) * size.y;
                cell.anchoredPosition = pos;
                //线性方程（y = k*x + b）
                float scale = scaleLine.k * pos.y + scaleLine.b;
                cell.localScale = Vector3.one * scale;
                if (preYs[i] > 0 && pos.y <= 0)
                {
                    cell.SetAsLastSibling();
                }
                if (preYs[i] < 0 && pos.y >= 0)
                {
                    cell.SetAsFirstSibling();
                }
                preYs[i] = pos.y;
            }

            if (!isInitedLayer)
            {
                for (int i = 0; i < cells.Length; i++)
                {
                    RectTransform cell = cells[i];
                    if (cell.anchoredPosition.y <= 0)
                    {
                        cell.SetAsLastSibling();
                    }
                    if (cell.anchoredPosition.y >= 0)
                    {
                        cell.SetAsFirstSibling();
                    }
                }
                isInitedLayer = true;
            }

            if (!isRunning)
            {
                angle += autoSpeed * Time.deltaTime;
            }
            if (runningFalseTimer > 0)
            {
                runningFalseTimer -= Time.deltaTime;
                if (runningFalseTimer <= 0f)
                {
                    isRunning = false;
                }
            }

            if (isCurveRunning)
            {
                if (duration <= 0)
                {
                    Log.Error("duration必须大于0");
                    isCurveRunning = false;
                    return;
                }
                if (curveTime < duration)
                {
                    curveTime += Time.deltaTime;
                    if (curveTime > duration) curveTime = duration;
                }
                else
                {
                    task.SetResult();
                    runningFalseTimer = 2;
                    isCurveRunning = false;
                }
                float curveValue = rotateCurve.Evaluate(curveTime / duration);
                angle = fromAngle + (toAngle - fromAngle) * curveValue;
            }
        }

        /// <summary>
        /// 开始滚动抽奖，传入需要停止的目标角度, 停止后结束Task
        /// </summary>
        public async ETTask StartRun(float stopAngle)
        {
            if (isRunning) return;
            isRunning = true;
            task = ETTask.Create();
            round = 5;
            fromAngle = Angle0To360(angle) - 360 * round;
            toAngle = Angle0To360(stopAngle);
            if (useCurve)
            {
                curveTime = 0;
                isCurveRunning = true;
            }
            else
            {
                preAngle = angle;
                DOVirtual.Float(fromAngle, toAngle, duration, (float angle) =>
                {
                    this.angle = angle;

                    //监听转过一个Cell的角度就触发事件（可用于播放音效或模拟每个Cell碰到撞针）
                    float offset = GetIncludeAngle(preAngle, angle);
                    preAngle = angle;
                    cellRadianListen += offset;
                    if (cellRadianListen >= deltaAngle)
                    {
                        cellRadianListen = 0;
                        onRotatedCell?.Invoke();
                    }
                }).SetEase(Ease.OutQuad).OnComplete(() =>
                {
                    task.SetResult();
                    runningFalseTimer = 2;
                }).Play();
            }
            await task;
        }

        private float GetIncludeAngle(float angle1, float angle2)
        {
            float includeAngle = Mathf.Abs(Angle0To360(angle1) - Angle0To360(angle2));
            if (includeAngle > Mathf.PI)
            {
                includeAngle = 360 - includeAngle;
            }
            return includeAngle;
        }

        private float Angle0To360(float angle)
        {
            while (angle < 0) angle += 360;
            while (angle > 360) angle -= 360;
            return angle;
        }
    }
}
